/************************************************************************
* AIDE - Automata Identification Engine
* Copyright (C) 2012-2013 Ali Khalili (khalili_DOT_ir_@SIGN_gmail_DOT_com)
* **********************************************************************
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License (LGPL) published 
* by the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version. This program is distributed without any
* warranty. See the GNU Lesser General Public License for more details.
* ***********************************************************************
* File         :     AIDE\Filters\IAs\Filter_IA_LearningPurpose.cs
* Description  :     Providing the learning purpose for IAs
* Change log   :     
************************************************************************/

using System;
using SMLib.Automata.IA;
using SMLib.Automata.Base;

/// <summary>
/// Provides filters for interface automata learning
/// </summary>
namespace AIDE.Filters.IA
{
	/// <summary>
	/// IA Learning Purpose
	/// </summary>
	/// <exception cref='NotEnabledActionException'>
	/// Is thrown when the not enabled action exception.
	/// </exception>
	public class Filter_IA_LearningPurpose: IBlackBox_IA
	{
		InterfaceAutomaton machine;
		InterfaceAutomaton.IA_State currentState;
		IBlackBox_IA SUL;
			
		/// <summary>
		/// Checks the conditions:
		/// It can limit inputs but should not limit outputs!
		/// </summary>
		/// <returns>
		/// True if the machine was OK as a "filter", false in other case
		/// </returns>
		public bool CheckConditions ()
		{
			return true;
		}
			
		/// <summary>
		/// Initializes a new instance of the <see cref="AIDE.Filters.IA.Filter_IA_LearningPurpose"/> class.
		/// </summary>
		/// <param name='machine'>
		/// The filter
		/// Machine.
		/// </param>
		public Filter_IA_LearningPurpose (InterfaceAutomaton machine, IBlackBox_IA SUL)
		{
			//* Should we have JUST ONE filer or a set of filters? In second case, should we
			//* serialize the filters OR all should be implemented in one class?
			this.machine = machine;
			this.SUL = SUL;
			currentState = this.machine.InitialState;
				
			CheckConditions ();
		}
	
			#region IBlackBox_IA implementation
		public void Reset ()
		{
			currentState = this.machine.InitialState;
			SUL.Reset ();
		}
			
			
		/// <summary>
		/// Perform an input action and not allow "illegal" actions to be performed
		/// </summary>
		/// <param name='input'>
		/// the input
		/// </param>
		public void Perform (ISymbol input)
		{
			bool hasStarInp = false;
			int starIndex = -1;
				
			for (int i=0; i<currentState.Transitions.Count; i++) {
				ISymbol s = (ISymbol)currentState.Transitions [i].Label;
				bool actionIsInp = (currentState.Transitions [i].StepType == InterfaceAutomaton.IA_Step.Step_Type.ACTION);
					
				if (actionIsInp && s.Equals (input)) {
					currentState = currentState.Transitions [i].Destination;
					SUL.Perform (input);
					return;
				} else if (actionIsInp && s.Equals ("*")) {
					hasStarInp = true;
					starIndex = i;
				}
			}
			if (hasStarInp) {
				currentState = currentState.Transitions [starIndex].Destination;
				SUL.Perform (input);
			} else
				throw new NotEnabledActionException ("");
		}
	
		public bool EventAvailable ()
		{
			return SUL.EventAvailable ();
		}
	
		public ISymbol Event ()
		{
			ISymbol o = SUL.Event ();
			int starIndex = -1;
			bool transDone = false;
					
			for (int i=0; i<currentState.Transitions.Count; i++) {
				ISymbol s = (ISymbol)currentState.Transitions [i].Label;
				bool actionIsOp = (currentState.Transitions [i].StepType == InterfaceAutomaton.IA_Step.Step_Type.ACTION);
				
				if (actionIsOp && s.Equals (o)) {
					currentState = currentState.Transitions [i].Destination;
					transDone = true;
					break;
				} else if (actionIsOp && s.Equals ("*")) {
					starIndex = i;
				}
			}
			if (!transDone) {
				if (starIndex == -1)
					throw new Exception ("Error!");
				currentState = currentState.Transitions [starIndex].Destination;				
			}
			return o;			
		}
	
		public Alphabet InputAlphabet {
			get {
				return SUL.InputAlphabet;
			}
		}
			#endregion
	}
}
	
